/*
 * Copyright (C) 2010 Moduad Co., Ltd.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package com.jxt.mina.net;

import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.ssl.SslFilter;

import com.jxt.mina.session.Session;


/**
 * This class represents a XMPP connection on the server.
 * 
 * @author Sehwan Noh (devnoh@gmail.com)
 */
public class Connection {

    private static final Log log = LogFactory.getLog(Connection.class);

    private IoSession ioSession;

    private Session session;

    private ConnectionCloseListener closeListener;

//    private int majorVersion = 1;
//
//    private int minorVersion = 0;
//
//    private String language = null;
//
//    private TLSPolicy tlsPolicy = TLSPolicy.optional;
//
//    @SuppressWarnings("unchecked")
//    private static ThreadLocal encoder = new ThreadLocalEncoder();

    private boolean closed;

    /**
     * Constructor.
     * 
     * @param ioSession the IoSession
     */
    public Connection(IoSession ioSession) {
        this.ioSession = ioSession;
        this.setClosed(false);
    }

	public boolean isClosed() {
		return closed;
	}

	public void setClosed(boolean closed) {
		this.closed = closed;
	}
    
	
	public void init(Session session) {
	  this.session = session;
	}
	
	
	
	
  /**
  * Closes the session including associated socket connection,
  * notifing all listeners that the channel is shutting down.
  */
 public void close() {
     boolean closedSuccessfully = false;
     synchronized (this) {
         if (!isClosed()) {
             try {
                 //deliverRawText("</stream:stream>", false);
             } catch (Exception e) {
                 // Ignore
             }
             if (session != null) {
                 session.setStatus(Session.STATUS_CLOSED);
             }
             ioSession.close(false);
             closed = true;
             closedSuccessfully = true;
         }
     }
     if (closedSuccessfully) {
        // notifyCloseListeners();
     }
 }
 
 
 
	 
	/**
	* Registers a listener for close event notification.
	* 
	* @param listener the listener to register for close events.
	*/
	public void registerCloseListener(ConnectionCloseListener listener) {
	  if (closeListener != null) {
	      throw new IllegalStateException("Close listener already configured");
	  }
	  if (isClosed()) {
	      listener.onConnectionClose(session);
	  } else {
	      closeListener = listener;
	  }
	}
	
	/**
	*  Removes a registered close event listener.
	*  
	* @param listener the listener to unregister for close events.
	*/
	public void unregisterCloseListener(ConnectionCloseListener listener) {
	  if (closeListener == listener) {
	      closeListener = null;
	  }
	}
    

//    //    /**
//    //     * Verifies that the connection is still live.
//    //     * 
//    //     * @return true if the socket remains valid, false otherwise.
//    //     */
//    //    public boolean validate() {
//    //        if (isClosed()) {
//    //            return false;
//    //        }
//    //        deliverRawText(" ");
//    //        return !isClosed();
//    //    }
//
//    /**
//     * Closes the session including associated socket connection,
//     * notifing all listeners that the channel is shutting down.
//     */
//    public void close() {
//        boolean closedSuccessfully = false;
////        synchronized (this) {
////            if (!isClosed()) {
////                try {
////                    deliverRawText("</stream:stream>", false);
////                } catch (Exception e) {
////                    // Ignore
////                }
////                if (session != null) {
////                    session.setStatus(Session.STATUS_CLOSED);
////                }
////                ioSession.close(false);
////                closed = true;
////                closedSuccessfully = true;
////            }
////        }
////        if (closedSuccessfully) {
////            notifyCloseListeners();
////        }
//    }
//
//    /**
//     * Sends notification message indicating that the server is being shutdown.
//     */
//    public void systemShutdown() {
////        deliverRawText("<stream:error><system-shutdown "
////                + "xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>");
////        close();
//    }
//
//    /**
//     * Initializes the connection with it's owning session.
//     * 
//     * @param session the session that owns this connection
//     */
//    public void init(Session session) {
//        this.session = session;
//    }
//
//    /**
//     * Returns true if the connection is closed.
//     * 
//     * @return true if the connection is closed, false otherwise.
//     */
//    public boolean isClosed() {
//        if (session == null) {
//            return closed;
//        }
//        return session.getStatus() == Session.STATUS_CLOSED;
//    }
//
//    //    /**
//    //     * Returns true if this connection is secure.
//    //     * 
//    //     * @return true if the connection is secure
//    //     */
//    //    public boolean isSecure() {
//    //        return ioSession.getFilterChain().contains("tls");
//    //    }
//
//    /**
//     * Registers a listener for close event notification.
//     * 
//     * @param listener the listener to register for close events.
//     */
//    public void registerCloseListener(ConnectionCloseListener listener) {
//        if (closeListener != null) {
//            throw new IllegalStateException("Close listener already configured");
//        }
//        if (isClosed()) {
//            listener.onConnectionClose(session);
//        } else {
//            closeListener = listener;
//        }
//    }
//
//    /**
//     *  Removes a registered close event listener.
//     *  
//     * @param listener the listener to unregister for close events.
//     */
//    public void unregisterCloseListener(ConnectionCloseListener listener) {
//        if (closeListener == listener) {
//            closeListener = null;
//        }
//    }
//
//    private void notifyCloseListeners() {
//        if (closeListener != null) {
//            try {
//                closeListener.onConnectionClose(session);
//            } catch (Exception e) {
//                log.error("Error notifying listener: " + closeListener, e);
//            }
//        }
//    }
////
////    /**
////     * Delivers the packet to this connection (without checking the recipient).
////     * 
////     * @param packet the packet to deliver
////     */
////    public void deliver(Packet packet) {
////        log.debug("SENT: " + packet.toXML());
////        if (!isClosed()) {
////            IoBuffer buffer = IoBuffer.allocate(4096);
////            buffer.setAutoExpand(true);
////
////            boolean errorDelivering = false;
////            try {
////                XMLWriter xmlSerializer = new XMLWriter(new IoBufferWriter(
////                        buffer, (CharsetEncoder) encoder.get()),
////                        new OutputFormat());
////                xmlSerializer.write(packet.getElement());
////                xmlSerializer.flush();
////                buffer.flip();
////                ioSession.write(buffer);
////            } catch (Exception e) {
////                log.debug("Connection: Error delivering packet" + "\n"
////                        + this.toString(), e);
////                errorDelivering = true;
////            }
////            if (errorDelivering) {
////                close();
////            } else {
////                session.incrementServerPacketCount();
////            }
////        }
////    }
////
////    /**
////     * Delivers raw text to this connection (in asynchronous mode).
////     * 
////     * @param text the XML stanza string to deliver
////     */
////    public void deliverRawText(String text) {
////        deliverRawText(text, true);
////    }
//
////    private void deliverRawText(String text, boolean asynchronous) {
////        log.debug("SENT: " + text);
////        if (!isClosed()) {
////            IoBuffer buffer = IoBuffer.allocate(text.length());
////            buffer.setAutoExpand(true);
////
////            boolean errorDelivering = false;
////            try {
////                buffer.put(text.getBytes("UTF-8"));
////                buffer.flip();
////                if (asynchronous) {
////                    ioSession.write(buffer);
////                } else {
////                    // Send stanza and wait for ACK
////                    boolean ok = ioSession.write(buffer).awaitUninterruptibly(
////                            Config.getInt("connection.ack.timeout", 2000));
////                    if (!ok) {
////                        log.warn("No ACK was received when sending stanza to: "
////                                + this.toString());
////                    }
////                }
////            } catch (Exception e) {
////                log.debug("Connection: Error delivering raw text" + "\n"
////                        + this.toString(), e);
////                errorDelivering = true;
////            }
////            // Close the connection if delivering text fails
////            if (errorDelivering && asynchronous) {
////                close();
////            }
////        }
////    }
////
////    public void startTLS(ClientAuth authentication) throws Exception {
////        log.debug("startTLS()...");
////        KeyStore ksKeys = SSLConfig.getKeyStore();
////        String keypass = SSLConfig.getKeyPassword();
////
////        KeyStore ksTrust = SSLConfig.getc2sTrustStore();
////        String trustpass = SSLConfig.getc2sTrustPassword();
////
////        KeyManager[] km = SSLKeyManagerFactory.getKeyManagers(ksKeys, keypass);
////        TrustManager[] tm = SSLTrustManagerFactory.getTrustManagers(ksTrust,
////                trustpass);
////
////        SSLContext tlsContext = SSLContext.getInstance("TLS");
////        tlsContext.init(km, tm, null);
////
////        SslFilter filter = new SslFilter(tlsContext);
////        ioSession.getFilterChain().addFirst("tls", filter);
////        //ioSession.getFilterChain().addBefore("executor", "tls", filter);
////        ioSession.setAttribute(SslFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
////
////        deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
////    }
//
//    /**
//     * Returns the IP address.
//     * 
//     * @return the IP address
//     * @throws UnknownHostException if IP address of host could not be determined.
//     */
//    public String getHostAddress() throws UnknownHostException {
//        return ((InetSocketAddress) ioSession.getRemoteAddress()).getAddress()
//                .getHostAddress();
//    }
//
//    /**
//     * Gets the host name for the IP address.
//     * 
//     * @return the host name for this IP address
//     * @throws UnknownHostException  if IP address of host could not be determined.
//     */
//    public String getHostName() throws UnknownHostException {
//        return ((InetSocketAddress) ioSession.getRemoteAddress()).getAddress()
//                .getHostName();
//    }
//
//    /**
//     * Returns the major version of XMPP being used by this connection.
//     * 
//     * @return the major XMPP version
//     */
//    public int getMajorXMPPVersion() {
//        return majorVersion;
//    }
//
//    /**
//     * Returns the minor version of XMPP being used by this connection.
//     * 
//     * @return the minor XMPP version
//     */
//    public int getMinorXMPPVersion() {
//        return minorVersion;
//    }
//
//    /**
//     * Sets the XMPP version information.
//     * 
//     * @param majorVersion the major version
//     * @param minorVersion the minor version
//     */
//    public void setXMPPVersion(int majorVersion, int minorVersion) {
//        this.majorVersion = majorVersion;
//        this.minorVersion = minorVersion;
//    }
//
//    /**
//     * Returns the language code that should be used for this connection.
//     * 
//     * @return the language code
//     */
//    public String getLanguage() {
//        return language;
//    }
//
//    /**
//     * Sets the language code that should be used for this connection.
//     * 
//     * @param language the language code
//     */
//    public void setLanaguage(String language) {
//        this.language = language;
//    }
//
//    @SuppressWarnings("unchecked")
//    private static class ThreadLocalEncoder extends ThreadLocal {
//        protected Object initialValue() {
//            return Charset.forName("UTF-8").newEncoder();
//        }
//    }
//
//    public TLSPolicy getTlsPolicy() {
//        return tlsPolicy;
//    }
//
//    public void setTlsPolicy(TLSPolicy tlsPolicy) {
//        this.tlsPolicy = tlsPolicy;
//    }
//
//    /**
//     * Enumeration of possible TLS policies required to interact with the server.
//     */
//    public enum TLSPolicy {
//        required, optional, disabled
//    }
//
//    /**
//     * Enumeration that specifies if clients should be authenticated (and how)
//     * while negotiating TLS.
//     */
//    public enum ClientAuth {
//        disabled, wanted, needed
//    }

}
